﻿using JWTAuthentication.Models;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;

namespace JWTAuthentication.Helper {
    /// <summary>
    /// Jwt帮助类
    /// </summary>
    public class JwtHelper {
        /// <summary>
        /// 颁发JWT字符串
        /// </summary>
        /// <param name="tokenModel"></param>
        /// <returns></returns>
        public static string IssueJWT(TokenModel tokenModel) {
            //配置
            JwtAuthConfig jwtAuthConfig = ConfigHelper.GetConfig<JwtAuthConfig>(JwtAuthConfig.ConfigName);
            var dateTime = DateTime.UtcNow;
            //JWT添加自定义的参数
            var claims = new List<Claim>()
            {
                //jwt签发者
                new Claim(JwtRegisteredClaimNames.Iss, jwtAuthConfig.Issuer),
                //jwt的接收方
                new Claim(JwtRegisteredClaimNames.Aud, jwtAuthConfig.Audience),
                //jwt的确认时间 (Issued At)
                new Claim(JwtRegisteredClaimNames.Iat, dateTime.ToString(), ClaimValueTypes.Integer64),
                //起始有效期(Not Before)
                new Claim(JwtRegisteredClaimNames.Nbf,$"{new DateTimeOffset(dateTime).ToUnixTimeSeconds()}") ,
                //结束有效期(Expiration Time)
                new Claim (JwtRegisteredClaimNames.Exp,$"{new DateTimeOffset(dateTime.AddMinutes(jwtAuthConfig.WebExp)).ToUnixTimeSeconds()}"),
                //用户Id(JWT ID)
                new Claim(JwtRegisteredClaimNames.Jti, tokenModel.Uid.ToString()),
            };
            //添加自定义参数
            //用户Id
            claims.Add(new Claim(ClaimTypes.Sid, tokenModel.Uid.ToString()));
            //用户名称
            claims.Add(new Claim(ClaimTypes.Name, tokenModel.Uname));
            //性别
            claims.Add(new Claim(ClaimTypes.Gender, tokenModel.Gender));
            //邮箱
            claims.Add(new Claim(ClaimTypes.Email, tokenModel.Email));

            //填充角色,用于角色授权 [Authorize(Roles = "Admin")]
            claims.Add(new Claim(ClaimTypes.Role, tokenModel.Role));
            //秘钥
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtAuthConfig.SecurityKey));
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            //过期时间
            double exp = 0;
            switch (tokenModel.TokenType) {
                case "Web":
                    exp = jwtAuthConfig.WebExp;
                    break;

                case "App":
                    exp = jwtAuthConfig.AppExp;
                    break;

                case "MiniProgram":
                    exp = jwtAuthConfig.MiniProgramExp;
                    break;

                case "Other":
                    exp = jwtAuthConfig.OtherExp;
                    break;
            }
            var jwt = new JwtSecurityToken(
                    issuer: jwtAuthConfig.Issuer,//签发者
                    audience: jwtAuthConfig.Audience,//jwt的接收方
                    claims: claims, //自定义参数
                    notBefore: dateTime,//起始有效期
                    expires: dateTime.AddHours(exp),//结束有效期
                    signingCredentials: creds//密钥
                );

            var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);

            return "Bearer " + encodedJwt;
        }

        /// <summary>
        /// 解析
        /// </summary>
        /// <param name="jwtStr"></param>
        /// <returns></returns>
        public static TokenModel SerializeJWT(string jwtStr) {
            var jwtHandler = new JwtSecurityTokenHandler();
            try {
                JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(jwtStr);
                object uname = new object();
                object gender = new object();
                object email = new object();
                object role = new object();
                jwtToken.Payload.TryGetValue(ClaimTypes.Name, out uname);
                jwtToken.Payload.TryGetValue(ClaimTypes.Gender, out gender);
                jwtToken.Payload.TryGetValue(ClaimTypes.Email, out email);
                jwtToken.Payload.TryGetValue(ClaimTypes.Role, out role);

                var tm = new TokenModel {
                    Uid = int.Parse(jwtToken.Id),
                    Uname = uname.ToString(),
                    Gender = gender.ToString(),
                    Email = email.ToString(),
                    Role = role.ToString(),
                    TokenType = "WebExp"
                };
                return tm;
            }
            catch (Exception e) {
                Console.WriteLine(e);
                throw;
            }
        }
    }
}
